// frame includes
#include "form.h"
#include "athread.h"
#include <QTime>
#include <QDebug>
#include <QFileDialog>
#include <QApplication>

// c++ includes
#include <math.h>
#include <memory>
#include <string>
#include <vector>
#include <fstream>
#include <dirent.h>
#include <iostream>
#include <sys/time.h>

// ITK includes
#include <itkImage.h>
#include <itkGDCMImageIO.h>
#include <itkMetaDataObject.h>
#include <itkImageFileReader.h>
#include <itkImageSeriesReader.h>
#include <itkGDCMSeriesFileNames.h>
#include <itkImageToVTKImageFilter.h>
#include <itkIntensityWindowingImageFilter.h>

// VTK includes
#include <vtkImageData.h>
#include <vtkSTLWriter.h>
#include <vtkPointData.h>
#include <vtkImageCast.h>
#include <vtkDoubleArray.h>
#include <vtkMassProperties.h>
#include <vtkMarchingCubes.h>
#include <vtkSmoothPolyDataFilter.h>

Form::Form(QString str): filepath(str) {
    qDebug() << "----------------开始处理----------------";
    qDebug() << "输入路径" << str;
    /*
     * ITK 读取文件并调整窗口信息
     * ITK 转 VTK 获得图像间隔以及给线程参数
    */
    if(str.isEmpty()){
        qDebug() << "错误，路径为空";
        qDebug() << "--------------------------------";
        qApp->exit();
    }
    using PixelType = float;
    constexpr unsigned int Dimension = 3;
    using ImageType = itk::Image< PixelType, Dimension >;
    using ReaderType = itk::ImageSeriesReader< ImageType >;
    using ImageIOType = itk::GDCMImageIO;
    using NamesGeneratorType = itk::GDCMSeriesFileNames;
    typedef itk::IntensityWindowingImageFilter <ImageType> IntensityWindowingImageFilterType;
    IntensityWindowingImageFilterType::Pointer intensityFilter = IntensityWindowingImageFilterType::New();
    ReaderType::Pointer reader = ReaderType::New();
    ImageIOType::Pointer dicomIO = ImageIOType::New();
    reader->SetImageIO(dicomIO);
    NamesGeneratorType::Pointer nameGenerator = NamesGeneratorType::New();
    nameGenerator->SetUseSeriesDetails(true);
    nameGenerator->SetDirectory(filepath.toLocal8Bit().data());
    using SeriesIdContainer = std::vector< std::string >;
    const SeriesIdContainer &seriesUID = nameGenerator->GetSeriesUIDs();
    auto seriesItr = seriesUID.begin();
    auto seriesEnd = seriesUID.end();
    using FileNamesContainer = std::vector< std::string >;
    FileNamesContainer fileNames ;
    std::string seriesIdentifier;
    while (seriesItr != seriesEnd) {
        seriesIdentifier = seriesItr->c_str();
        fileNames = nameGenerator->GetFileNames(seriesIdentifier);
        ++seriesItr;
    }
    reader->SetFileNames(fileNames);
    try {
        reader->Update();
    } catch (itk::ExceptionObject &ex) {
        qDebug() << "错误，读取失败";
        std::cout << ex << std::endl;
        qDebug() << "--------------------------------";
        qApp->exit();
    }
    intensityFilter->SetInput(reader->GetOutput());
    intensityFilter->SetWindowMinimum(-200);
    intensityFilter->SetWindowMaximum(400);
    intensityFilter->SetOutputMinimum(0);
    intensityFilter->SetOutputMaximum(1);
    intensityFilter->Update();
    typedef itk::ImageToVTKImageFilter< ImageType> itkTovtkFilterType;
    itkTovtkFilterType::Pointer itkTovtkImageFilter = itkTovtkFilterType::New();
    itkTovtkImageFilter->SetInput(intensityFilter->GetOutput());
    itkTovtkImageFilter->Update();
    vtkNew<vtkImageData> imagedata_;
    imagedata_->DeepCopy(itkTovtkImageFilter->GetOutput());
    imagedata_->GetSpacing(Spacing);
    /*
     * 打印病人姓名
    */
    constexpr unsigned int dimension_label = 2;
    using ImageType_label = itk::Image<PixelType, dimension_label>;
    using ReaderType_label = itk::ImageFileReader<ImageType_label>;
    using ImageIOType = itk::GDCMImageIO;
    ReaderType_label::Pointer reader_label = ReaderType_label::New();
    ImageIOType::Pointer dicom_io = ImageIOType::New();
    reader_label->SetFileName(fileNames.at(10));
    reader_label->SetImageIO(dicom_io);
    try {
        reader_label->Update();
        using DictionaryType = itk::MetaDataDictionary;
        const DictionaryType &dictionary = reader_label->GetMetaDataDictionary();
        using MetaDataStringType = itk::MetaDataObject<std::string>;
        for (auto ite = dictionary.Begin(); ite != dictionary.End(); ++ite) {
            QString id = QString::fromStdString(ite->first);
            itk::MetaDataObjectBase::Pointer entry = ite->second;
            MetaDataStringType::ConstPointer entry_value =
                    dynamic_cast<const MetaDataStringType *>(ite->second.GetPointer());
            std::string key_string;
            itk::GDCMImageIO::GetLabelFromTag(id.toStdString().c_str(), key_string);
            if(QString::fromStdString(key_string)=="Patient's Name"){
                qDebug() << "病人姓名:"<<
                            QString::fromStdString(
                                entry_value->GetMetaDataObjectValue());
                break;
            }
        }
    } catch (itk::ExceptionObject &ex) {
        qDebug() << "警告，病人信息提取错误";
        std::cout << ex << std::endl;
        qDebug() << "dicom open fail " << ex.GetDescription();
        qDebug() << "--------------------------------";
    }

    /*
     * 根据文件数量分配线程
    */
    num = fileNames.size();
    qDebug() << "文件数量" << num;
    if (num < 50) {
        qDebug() << "错误，图片数量太少";
        qDebug() << "--------------------------------";
        qApp->exit();
    }
    datasize = 262144 * num;
    testshortarr = new double[datasize];
    /*
     * 线程开启
    */
    for (int gpu_thread_number = 0; gpu_thread_number < 8; ++gpu_thread_number) {
        qDebug() << QString("GPU   %1  线程启动").arg(gpu_thread_number);
        int sum_dcm = (num - 40) / 8;
        int begin_dcm = 40 + sum_dcm * gpu_thread_number;
        MyThread *tmpthread = new MyThread;
        tmpthread->SetLStruct(
                    begin_dcm, sum_dcm,
                    begin_dcm * 512 * 512,
                    gpu_thread_number % 4,
                    testshortarr,
                    imagedata_);
        connect(tmpthread, &MyThread::SignalThreadEnd, this, &Form::result);
        tmpthread->start();
    }
    qDebug() << "----------------开始计时----------------";
    qDebug() << "运算开始...";
    time.start();
}

Form::~Form() {
}

void Form::result() {
    static int result = 0;
    ++result;
    if (8 == result) {
        qDebug() << "模型生成中...";
        vtkNew<vtkMarchingCubes> marchingcubes ;
        vtkNew<vtkSmoothPolyDataFilter> smoothfilter;
        vtkNew<vtkSTLWriter> vtk_writer_stl;
        vtkNew<vtkImageData>reader_data;
        vtkDoubleArray *tempimarr2 = vtkDoubleArray::New();
        vtkNew<vtkImageCast> imagedata;
        vtkNew<vtkMassProperties> massProperties;
        tempimarr2->SetVoidArray(testshortarr, datasize, 1);
        reader_data ->SetDimensions(512, 512, num);
        reader_data->SetSpacing(Spacing[0] * 100, Spacing[1] * 100, Spacing[2] * 100);
        reader_data->GetPointData()->SetScalars(tempimarr2);
        reader_data->Modified();
        imagedata->SetInputData(reader_data);
        imagedata->SetOutputScalarTypeToFloat();
        marchingcubes->SetInputConnection(imagedata->GetOutputPort());
        marchingcubes->SetValue(0, 1);
        massProperties->SetInputConnection(marchingcubes->GetOutputPort());
        massProperties->Update();
        qDebug() << "耗时" << time.elapsed() / 1000.0 << "s" ;
        qDebug() << QString("体积 %1 立方厘米").arg(massProperties->GetVolume() / 1000000000, 0, 'f', 2);
        qDebug() << "tmp.stl 文件保存在输入目录";
        qDebug() << "----------------end----------------";
        vtk_writer_stl->SetInputConnection(marchingcubes->GetOutputPort());
        vtk_writer_stl->SetFileName(QString(filepath + "/tmp.stl").toLocal8Bit().data());
        vtk_writer_stl->Write();
        qApp->exit();
    }
}


































